﻿using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Reflection;
using System.Windows.Forms;
using VSSDKAssist.Platform.Helpers;
using VSSDKAssist.Platform.ToolWindows.UI;
using VSSDKAssist.Platform.Enums;
using System.Security;
using System.Security.Permissions;
using System.Security.Policy;
using System.IO;
using EnvDTE;

namespace VSSDKAssist.Platform.ToolWindows
{
	public partial class PackageBrowserControl : UserControl
	{
		IServiceProvider serviceProvider;

		public PackageBrowserControl()
		{
			InitializeComponent();
		}

		public PackageBrowserControl(IServiceProvider provider)
		{
			this.serviceProvider = provider;
			InitializeComponent();
		}

		private void PackageBrowserControl_Load(object sender, EventArgs e)
		{
			SetControls();
			RefreshTree();
		}

		private void tsRefresh_Click(object sender, EventArgs e)
		{
			RefreshTree();
		}

		private void trvPackageAssets_AfterSelect(object sender, TreeViewEventArgs e)
		{
			if(e.Node is PackageNode)
			{
				LoadPackageInformation(e.Node);
			}
			else if(e.Node is ServiceNode)
			{
				LoadServiceInformation(e.Node);
			}
			else if(e.Node is ToolWindowNode)
			{
				LoadToolWindowInformation(e.Node);
			}
			else if(e.Node is CommandNode)
			{
				LoadCommandInformation();
			}
			else
			{
				ShowInformationGroupBoxes(false);
			}
		}

		#region Private Implementation
		private void SetControls()
		{
			trvPackageAssets.DrawMode = TreeViewDrawMode.OwnerDrawAll;
			trvPackageAssets.ImageIndex = trvPackageAssets.ImageList.Images.Count;
			trvPackageAssets.SelectedImageIndex = trvPackageAssets.ImageList.Images.Count;
		}

		private void ShowInformationGroupBoxes(bool show)
		{
			grpPackageInformation.Visible = show;
			grpServiceInformation.Visible = show;
			grpToolwindowInformation.Visible = show;
			grpCommandInformation.Visible = show;
		}

		private void RenderTree(IList<Type> packageTypes)
		{
			trvPackageAssets.Nodes.Clear();

			TreeNode packagesNode = new TreeNode("Packages", ExtendedTreeView.NoImage, ExtendedTreeView.NoImage);

			foreach(Type packageType in packageTypes)
			{
				PackageNode packageNode = new PackageNode(packageType.Name, packageType);

				packageNode.Nodes.Add(GetServicesNodes(packageType));
				packageNode.Nodes.Add(GetToolWindowNodes(packageType));

				//TODO: Get Commands
				//packageNode.Nodes.Add(GetCommandsNodes());

				packagesNode.Nodes.Add(packageNode);
			}

			trvPackageAssets.Nodes.Add(packagesNode);

			trvPackageAssets.Nodes[0].ExpandAll();
		}

		private TreeNode GetServicesNodes(Type packageType)
		{
			TreeNode servicesNode = new TreeNode("Services", ExtendedTreeView.NoImage, ExtendedTreeView.NoImage);

			ProvideServiceAttribute[] provideServiceAttributes = GetAttributes<ProvideServiceAttribute>(packageType, true);

			foreach(ProvideServiceAttribute provideServiceAttribute in provideServiceAttributes)
			{
				ServiceNode serviceNode = new ServiceNode(provideServiceAttribute.ServiceName, provideServiceAttribute);
				servicesNode.Nodes.Add(serviceNode);
			}

			return servicesNode;
		}

		private TreeNode GetToolWindowNodes(Type packageType)
		{
			TreeNode toolWindowsNode = new TreeNode("ToolWindows", ExtendedTreeView.NoImage, ExtendedTreeView.NoImage);

			ProvideToolWindowAttribute[] provideToolWindowAttributes = GetAttributes<ProvideToolWindowAttribute>(packageType, true);

			foreach(ProvideToolWindowAttribute provideToolWindowAttribute in provideToolWindowAttributes)
			{
				ToolWindowNode toolWindowNode = new ToolWindowNode(provideToolWindowAttribute.ToolType.Name, provideToolWindowAttribute);
				toolWindowsNode.Nodes.Add(toolWindowNode);
			}

			return toolWindowsNode;
		}

		private TreeNode GetCommandsNodes()
		{
			TreeNode commandsNode = new TreeNode("Commands", ExtendedTreeView.NoImage, ExtendedTreeView.NoImage);

			//TODO: Get Commands

			return commandsNode;
		}

		private void RefreshTree()
		{
			PackageTypeLocator locator = new PackageTypeLocator(this.serviceProvider);

			IList<Type> packageTypes = locator.LocatePackage();

			ShowInformationGroupBoxes(false);

			if(packageTypes.Count > 0)
			{
				RenderTree(packageTypes);
			}
			else
			{
				trvPackageAssets.Nodes.Clear();
			}
		}

		private void LoadPackageInformation(TreeNode node)
		{
			const string resxFileName = "VsPackage.resx";

			ShowInformationGroupBoxes(false);
			grpPackageInformation.Visible = true;

			PackageNode packageNode = node as PackageNode;
			Type package = node.Tag as Type;

			if(package != null)
			{
				DefaultRegistryRootAttribute defaultRegistryRootAttribute =
					GetAttribute<DefaultRegistryRootAttribute>(package, true);

				if(defaultRegistryRootAttribute != null)
				{
					txtRegistryRoot.Text = defaultRegistryRootAttribute.Root;
				}

				if(package.GUID != null)
				{
					txtPackageGuid.Text = package.GUID.ToString("D").ToUpper();
				}

				ProvideLoadKeyAttribute provideLoadKeyAttribute =
					GetAttribute<ProvideLoadKeyAttribute>(package, true);

				if(provideLoadKeyAttribute != null)
				{
					txtPackageMinimumVSEdition.Text = provideLoadKeyAttribute.MinimumEdition;
					txtVersion.Text = provideLoadKeyAttribute.ProductVersion;
					txtProductName.Text = provideLoadKeyAttribute.ProductName;
					txtCompanyName.Text = provideLoadKeyAttribute.CompanyName;
				}

				InstalledProductRegistrationAttribute installedProductRegistrationAttribute =
					GetAttribute<InstalledProductRegistrationAttribute>(package, true);

				if(installedProductRegistrationAttribute != null)
				{
					DTE dte = this.serviceProvider.GetService(typeof(DTE)) as DTE;

					ProjectItem resxProjectItem = dte.Solution.FindProjectItem(resxFileName);

					if(resxProjectItem != null)
					{
						string resxFile = resxProjectItem.Properties.Item("FullPath").Value.ToString();

						string productDetails = installedProductRegistrationAttribute.ProductDetails;

						if(productDetails != null)
						{
							productDetails = productDetails.Replace("#", "");

							string productDetailsValue =
								ResxHelper.GetResxValueById<string>(
									this.serviceProvider,
									resxFile,
									productDetails);

							txtDetailedInformation.Text = productDetailsValue;
						}

						Bitmap bitmap =
							ResxHelper.GetResxValueById<Bitmap>(
								this.serviceProvider,
								resxFile,
								installedProductRegistrationAttribute.IconResourceID.ToString());

						if(bitmap != null)
						{
							pctPackageIcon.Image = bitmap;
						}
					}
				}

				ProvideAutoLoadAttribute provideAutoLoadAttribute =
					GetAttribute<ProvideAutoLoadAttribute>(package, true);

				if(provideAutoLoadAttribute != null)
				{
					chkAutoload.Checked = true;
					txtAutoLoad.Visible = true;

					if(UIContextGuid.Values.ContainsKey(provideAutoLoadAttribute.LoadGuid))
					{
						txtAutoLoad.Text = UIContextGuid.Values[provideAutoLoadAttribute.LoadGuid];
					}
					else
					{
						txtAutoLoad.Text = provideAutoLoadAttribute.LoadGuid.ToString("D").ToUpper();
					}
				}
				else
				{
					chkAutoload.Checked = false;
					txtAutoLoad.Visible = false;
					txtAutoLoad.Text = string.Empty;
				}
			}
		}

		private void LoadServiceInformation(TreeNode node)
		{
			ShowInformationGroupBoxes(false);
			grpServiceInformation.Visible = true;

			ServiceNode serviceNode = node as ServiceNode;
			ProvideServiceAttribute provideServiceAttribute = node.Tag as ProvideServiceAttribute;

			txtServiceName.Text = provideServiceAttribute.Service.Name;
		}

		private void LoadCommandInformation()
		{
			ShowInformationGroupBoxes(false);
			grpCommandInformation.Visible = true;
		}

		private void LoadToolWindowInformation(TreeNode node)
		{
			ShowInformationGroupBoxes(false);
			grpToolwindowInformation.Visible = true;

			ToolWindowNode toolWindowNode = node as ToolWindowNode;
			ProvideToolWindowAttribute provideToolWindowAttribute = node.Tag as ProvideToolWindowAttribute;

			txtToolWindowName.Text = provideToolWindowAttribute.ToolType.Name;
			txtDockStyle.Text = provideToolWindowAttribute.Style.ToString();
			txtOrientation.Text = provideToolWindowAttribute.Orientation.ToString();

			if(!string.IsNullOrEmpty(provideToolWindowAttribute.Window))
			{
				Guid windowGuid = new Guid(provideToolWindowAttribute.Window);

				if(WindowGuid.Values.ContainsKey(windowGuid))
				{
					txtWindow.Text = WindowGuid.Values[windowGuid];
				}
				else
				{
					txtWindow.Text = provideToolWindowAttribute.Window.ToUpper();
				}
			}

			chkMultiInstance.Checked = provideToolWindowAttribute.MultiInstances;
			chkTransient.Checked = provideToolWindowAttribute.Transient;

			Type package = node.Parent.Parent.Tag as Type;

			ProvideToolWindowVisibilityAttribute[] provideToolWindowVisibilityAttributes = GetAttributes<ProvideToolWindowVisibilityAttribute>(package, true);

			foreach(ProvideToolWindowVisibilityAttribute provideToolWindowVisibilityAttribute in provideToolWindowVisibilityAttributes)
			{
				if(provideToolWindowVisibilityAttribute.Name.Equals(provideToolWindowAttribute.ToolType.Name))
				{
					if(UIContextGuid.Values.ContainsKey(provideToolWindowVisibilityAttribute.CommandUIGuid))
					{
						txtVisibility.Text = UIContextGuid.Values[provideToolWindowVisibilityAttribute.CommandUIGuid];
					}
					else
					{
						txtVisibility.Text = provideToolWindowVisibilityAttribute.CommandUIGuid.ToString("D").ToUpper();
					}

					break;
				}
			}
		}

		public static T GetAttribute<T>(ICustomAttributeProvider provider, bool inherit)
		{
			T[] attribs = GetAttributes<T>(provider, inherit);
			if(attribs != null && attribs.Length == 1)
			{
				return attribs[0];
			}
			return default(T);
		}

		public static T[] GetAttributes<T>(ICustomAttributeProvider provider, bool inherit)
		{
			object[] attribs = null;
			attribs = provider.GetCustomAttributes(typeof(T), inherit);
			if(attribs == null)
			{
				return new T[0];
			}

			return Array.ConvertAll<Object, T>(attribs, delegate(object attrib)
				{
					return (T)attrib;
				});
		}
		#endregion
	}
}